<template>
  <div class="slam-3d-visualization">
    <div class="bg-white rounded-2xl shadow-lg p-6 border border-gray-100">
      <div class="flex items-center justify-between mb-4">
        <h3 class="text-lg font-semibold text-gray-800 flex items-center">
          <i class="fa-solid fa-cube mr-2 text-purple-500"></i>
          SLAM 3D建图可视化
        </h3>
        <div class="flex items-center space-x-2">
          <span :class="getStatusColor(status)" class="px-2 py-1 rounded-full text-xs font-medium">
            {{ getStatusText(status) }}
          </span>
          <button 
            @click="toggleFullscreen" 
            class="px-3 py-1 bg-gray-500 text-white rounded text-xs hover:bg-gray-600 transition-colors"
          >
            <i class="fa-solid fa-expand mr-1"></i>
            全屏
          </button>
        </div>
      </div>
      
      <!-- 3D可视化区域 -->
      <div class="relative bg-gray-900 rounded-lg overflow-hidden mb-4" :class="fullscreen ? 'fixed inset-0 z-50' : 'h-96'">
        <canvas ref="canvas3d" class="w-full h-full"></canvas>
        
        <!-- 加载状态 -->
        <div v-if="loading" class="absolute inset-0 flex items-center justify-center bg-black/50">
          <div class="text-center text-white">
            <i class="fa-solid fa-spinner fa-spin text-2xl mb-2"></i>
            <p>正在加载3D场景...</p>
          </div>
        </div>
        
        <!-- 无数据状态 -->
        <div v-else-if="!hasData" class="absolute inset-0 flex items-center justify-center bg-gray-800">
          <div class="text-center text-white">
            <i class="fa-solid fa-cube text-4xl mb-4 text-gray-400"></i>
            <p class="text-lg mb-2">暂无3D数据</p>
            <p class="text-sm text-gray-400">请启动SLAM建图会话</p>
          </div>
        </div>
        
        <!-- 控制面板覆盖层 -->
        <div v-if="hasData" class="absolute top-4 left-4 bg-black/70 rounded-lg p-3 text-white">
          <div class="space-y-1 text-sm">
            <div class="flex items-center space-x-2">
              <i class="fa-solid fa-key text-red-400"></i>
              <span>关键帧: {{ slamData.keyframes || 0 }}</span>
            </div>
            <div class="flex items-center space-x-2">
              <i class="fa-solid fa-circle text-blue-400"></i>
              <span>地图点: {{ slamData.mappoints || 0 }}</span>
            </div>
            <div class="flex items-center space-x-2">
              <i class="fa-solid fa-route text-green-400"></i>
              <span>轨迹长度: {{ (slamData.trajectoryLength || 0).toFixed(2) }}m</span>
            </div>
          </div>
        </div>
        
        <!-- 视角控制面板 -->
        <div v-if="hasData" class="absolute top-4 right-4 bg-black/70 rounded-lg p-3 text-white">
          <div class="space-y-2 text-sm">
            <div class="flex items-center space-x-2">
              <span>视角:</span>
              <select v-model="cameraView" @change="changeCameraView" class="bg-black/50 text-white border border-gray-600 rounded px-2 py-1 text-xs">
                <option value="perspective">透视</option>
                <option value="top">俯视</option>
                <option value="side">侧视</option>
                <option value="front">前视</option>
              </select>
            </div>
            <div class="flex items-center space-x-2">
              <span>显示:</span>
              <div class="flex space-x-1">
                <button @click="toggleShowTrajectory" :class="showTrajectory ? 'bg-green-500' : 'bg-gray-500'" class="px-2 py-1 rounded text-xs">
                  轨迹
                </button>
                <button @click="toggleShowKeyframes" :class="showKeyframes ? 'bg-red-500' : 'bg-gray-500'" class="px-2 py-1 rounded text-xs">
                  关键帧
                </button>
                <button @click="toggleShowMappoints" :class="showMappoints ? 'bg-blue-500' : 'bg-gray-500'" class="px-2 py-1 rounded text-xs">
                  地图点
                </button>
              </div>
            </div>
          </div>
        </div>
        
        <!-- 退出全屏按钮 -->
        <button 
          v-if="fullscreen" 
          @click="exitFullscreen"
          class="absolute bottom-4 right-4 p-2 bg-black/70 text-white rounded-full hover:bg-black/80 transition-colors"
        >
          <i class="fa-solid fa-times"></i>
        </button>
      </div>
      
      <!-- 统计信息 -->
      <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
        <div class="bg-gradient-to-r from-red-50 to-red-100 rounded-lg p-4">
          <div class="flex items-center justify-between">
            <div>
              <p class="text-sm text-red-600">关键帧</p>
              <p class="text-2xl font-bold text-red-800">{{ slamData.keyframes || 0 }}</p>
            </div>
            <i class="fa-solid fa-key text-red-500 text-2xl"></i>
          </div>
        </div>
        
        <div class="bg-gradient-to-r from-blue-50 to-blue-100 rounded-lg p-4">
          <div class="flex items-center justify-between">
            <div>
              <p class="text-sm text-blue-600">地图点</p>
              <p class="text-2xl font-bold text-blue-800">{{ slamData.mappoints || 0 }}</p>
            </div>
            <i class="fa-solid fa-circle text-blue-500 text-2xl"></i>
          </div>
        </div>
        
        <div class="bg-gradient-to-r from-green-50 to-green-100 rounded-lg p-4">
          <div class="flex items-center justify-between">
            <div>
              <p class="text-sm text-green-600">轨迹长度</p>
              <p class="text-2xl font-bold text-green-800">{{ (slamData.trajectoryLength || 0).toFixed(2) }}m</p>
            </div>
            <i class="fa-solid fa-route text-green-500 text-2xl"></i>
          </div>
        </div>
        
        <div class="bg-gradient-to-r from-purple-50 to-purple-100 rounded-lg p-4">
          <div class="flex items-center justify-between">
            <div>
              <p class="text-sm text-purple-600">建图进度</p>
              <p class="text-2xl font-bold text-purple-800">{{ (slamData.progress || 0).toFixed(1) }}%</p>
            </div>
            <i class="fa-solid fa-chart-line text-purple-500 text-2xl"></i>
          </div>
        </div>
      </div>
      
      <!-- 控制按钮 -->
      <div class="flex space-x-2">
        <button 
          @click="startVisualization" 
          :disabled="!canStart"
          class="flex-1 px-4 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600 transition-colors disabled:opacity-50"
        >
          <i class="fa-solid fa-play mr-1"></i>
          开始可视化
        </button>
        
        <button 
          @click="resetView" 
          class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 transition-colors"
        >
          <i class="fa-solid fa-home mr-1"></i>
          重置视角
        </button>
        
        <button 
          @click="exportData" 
          :disabled="!hasData"
          class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors disabled:opacity-50"
        >
          <i class="fa-solid fa-download mr-1"></i>
          导出数据
        </button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, nextTick, computed, watch } from 'vue'
import axios from 'axios'
import * as THREE from 'three'

// 接收会话ID作为prop
const props = defineProps({
  sessionId: {
    type: Number,
    required: true
  }
})

// 响应式数据
const canvas3d = ref(null)
const loading = ref(false)
const hasData = ref(false)
const status = ref('STOPPED')
const fullscreen = ref(false)
const cameraView = ref('perspective')
const showTrajectory = ref(true)
const showKeyframes = ref(true)
const showMappoints = ref(true)

const slamData = ref({
  keyframes: 0,
  mappoints: 0,
  trajectoryLength: 0,
  progress: 0
})

// 3D渲染相关
let scene = null
let camera = null
let renderer = null
let controls = null
let trajectory = []
let keyframes = []
let mappoints = []

// 计算属性
const canStart = computed(() => {
  return status.value === 'STOPPED'
})

// 方法
const getStatusColor = (status) => {
  const colors = {
    'STARTING': 'bg-blue-100 text-blue-800',
    'RUNNING': 'bg-green-100 text-green-800',
    'PAUSED': 'bg-yellow-100 text-yellow-800',
    'STOPPED': 'bg-gray-100 text-gray-800',
    'ERROR': 'bg-red-100 text-red-800'
  }
  return colors[status] || 'bg-gray-100 text-gray-800'
}

const getStatusText = (status) => {
  const texts = {
    'STARTING': '启动中',
    'RUNNING': '运行中',
    'PAUSED': '暂停',
    'STOPPED': '已停止',
    'ERROR': '错误'
  }
  return texts[status] || status
}

const init3DScene = () => {
  if (!canvas3d.value) return
  
  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0x1a1a1a)
  
  // 创建相机
  camera = new THREE.PerspectiveCamera(75, canvas3d.value.clientWidth / canvas3d.value.clientHeight, 0.1, 1000)
  camera.position.set(10, 10, 10)
  camera.lookAt(0, 0, 0)
  
  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ canvas: canvas3d.value, antialias: true })
  renderer.setSize(canvas3d.value.clientWidth, canvas3d.value.clientHeight)
  renderer.shadowMap.enabled = true
  renderer.shadowMap.type = THREE.PCFSoftShadowMap
  
  // 添加光源
  const ambientLight = new THREE.AmbientLight(0x404040, 0.6)
  scene.add(ambientLight)
  
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
  directionalLight.position.set(20, 20, 10)
  directionalLight.castShadow = true
  scene.add(directionalLight)
  
  // 添加坐标轴
  const axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)
  
  // 添加网格
  const gridHelper = new THREE.GridHelper(20, 20, 0x444444, 0x444444)
  scene.add(gridHelper)
  
  // 添加控制器（简化版本，不依赖OrbitControls）
  // controls = new THREE.OrbitControls(camera, renderer.domElement)
  // controls.enableDamping = true
  // controls.dampingFactor = 0.05
  // controls.enableZoom = true
  // controls.enablePan = true
  
  // 渲染循环
  const animate = () => {
    requestAnimationFrame(animate)
    // controls.update()
    renderer.render(scene, camera)
  }
  animate()
  
  // 监听窗口大小变化
  const handleResize = () => {
    if (!canvas3d.value) return
    camera.aspect = canvas3d.value.clientWidth / canvas3d.value.clientHeight
    camera.updateProjectionMatrix()
    renderer.setSize(canvas3d.value.clientWidth, canvas3d.value.clientHeight)
  }
  
  window.addEventListener('resize', handleResize)
  
  // 清理函数
  onUnmounted(() => {
    window.removeEventListener('resize', handleResize)
    if (renderer) {
      renderer.dispose()
    }
  })
}

const loadSlamData = async () => {
  loading.value = true
  try {
    console.log('🎯 开始加载SLAM数据，会话ID:', props.sessionId)
    // 使用实时数据API获取SLAM数据
    const encodedSessionId = encodeURIComponent(props.sessionId)
    const response = await axios.get(`http://8.136.52.147:8100/api/sessions/${encodedSessionId}/data`)
    console.log('📡 API响应:', response.data)
    // 处理Python SLAM服务的响应格式 {success: true, data: {...}}
    if (response.data.success) {
      const data = response.data.data
      if (data) {
        hasData.value = true
        processSlamData(data)
        updateVisualization()
        console.log('✅ SLAM数据加载成功')
      } else {
        console.warn('⚠️ API返回数据为空')
      }
    } else {
      console.error('❌ API返回错误:', response.data.message)
    }
  } catch (error) {
    console.error('❌ 加载SLAM数据失败:', error)
  } finally {
    loading.value = false
  }
}

const processSlamData = (data) => {
  console.log('🎯 处理SLAM数据:', data)
  
  // 处理轨迹数据 - 从trajectory_points字段获取
  trajectory = data.trajectory_points || []
  
  // 处理关键帧数据 - 从camera_poses字段获取（每个位姿代表一个关键帧）
  keyframes = data.camera_poses || []
  
  // 处理地图点数据 - 从map_points_3d字段获取
  mappoints = data.map_points_3d || []
  
  // 更新统计信息
  slamData.value = {
    keyframes: data.keyframes || 0,  // 直接使用数字
    mappoints: data.map_points || 0,  // 直接使用数字
    trajectoryLength: data.trajectory_length || 0,
    progress: data.progress || 0
  }
  
  console.log('📊 处理后的数据:', {
    trajectory: trajectory.length,
    keyframes: keyframes.length,
    mappoints: mappoints.length,
    slamData: slamData.value
  })
  
  // 自动调整相机视角到轨迹范围
  if (trajectory.length > 0) {
    adjustCameraToTrajectory()
  }
}

const calculateTrajectoryLength = (trajectory) => {
  let length = 0
  for (let i = 1; i < trajectory.length; i++) {
    const dx = (trajectory[i].position?.x || 0) - (trajectory[i-1].position?.x || 0)
    const dy = (trajectory[i].position?.y || 0) - (trajectory[i-1].position?.y || 0)
    const dz = (trajectory[i].position?.z || 0) - (trajectory[i-1].position?.z || 0)
    length += Math.sqrt(dx*dx + dy*dy + dz*dz)
  }
  return length
}

// 自动调整相机视角到轨迹范围
const adjustCameraToTrajectory = () => {
  if (!camera || trajectory.length === 0) return
  
  // 计算轨迹的边界框
  let minX = Infinity, maxX = -Infinity
  let minY = Infinity, maxY = -Infinity
  let minZ = Infinity, maxZ = -Infinity
  
  trajectory.forEach(point => {
    const x = point.position?.x || 0
    const y = point.position?.y || 0
    const z = point.position?.z || 0
    minX = Math.min(minX, x)
    maxX = Math.max(maxX, x)
    minY = Math.min(minY, y)
    maxY = Math.max(maxY, y)
    minZ = Math.min(minZ, z)
    maxZ = Math.max(maxZ, z)
  })
  
  // 计算中心点和范围
  const centerX = (minX + maxX) / 2
  const centerY = (minY + maxY) / 2
  const centerZ = (minZ + maxZ) / 2
  const range = Math.max(maxX - minX, maxY - minY, maxZ - minZ)
  
  // 如果轨迹很小（接近原点），使用默认视角
  if (range < 0.1) {
    camera.position.set(5, 5, 5)
    camera.lookAt(0, 0, 0)
    return
  }
  
  // 调整相机位置，确保能看到整个轨迹
  const distance = range * 2
  camera.position.set(centerX + distance, centerY + distance, centerZ + distance)
  camera.lookAt(centerX, centerY, centerZ)
  
  console.log('📷 相机已调整到轨迹范围:', {
    center: [centerX, centerY, centerZ],
    range: range,
    cameraPos: camera.position
  })
}

const updateVisualization = () => {
  if (!scene || !camera || !renderer) return
  
  // 清除现有对象
  const objectsToRemove = []
  scene.traverse((child) => {
    if (child.userData.isSLAMObject) {
      objectsToRemove.push(child)
    }
  })
  objectsToRemove.forEach(obj => scene.remove(obj))
  
  // 添加轨迹线
  if (showTrajectory.value && trajectory.length > 0) {
    const geometry = new THREE.BufferGeometry()
    const positions = []
    
    trajectory.forEach((point) => {
      // 从position字段获取坐标
      const x = point.position?.x || 0
      const y = point.position?.y || 0
      const z = point.position?.z || 0
      positions.push(x, y, z)
    })
    
    geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3))
    
    const material = new THREE.LineBasicMaterial({ color: 0x00ff00, linewidth: 3 })
    const line = new THREE.Line(geometry, material)
    line.userData.isSLAMObject = true
    scene.add(line)
    
    console.log(`✅ 添加绿色轨迹线: ${trajectory.length}个点`)
  }
  
  // 添加关键帧
  if (showKeyframes.value && keyframes.length > 0) {
    keyframes.forEach((keyframe, index) => {
      const geometry = new THREE.SphereGeometry(0.3, 8, 6)
      const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
      const sphere = new THREE.Mesh(geometry, material)
      
      // 从position_3d字段获取坐标
      const pos = keyframe.position_3d || [0, 0, 0]
      sphere.position.set(pos[0] || 0, pos[1] || 0, pos[2] || 0)
      sphere.userData.isSLAMObject = true
      scene.add(sphere)
    })
    
    console.log(`✅ 添加关键帧: ${keyframes.length}个`)
  }
  
  // 添加地图点
  if (showMappoints.value && mappoints.length > 0) {
    mappoints.forEach(point => {
      const geometry = new THREE.SphereGeometry(0.05, 6, 4)
      const material = new THREE.MeshBasicMaterial({ color: 0x0000ff })
      const sphere = new THREE.Mesh(geometry, material)
      
      // 从position_3d字段获取坐标
      const pos = point.position_3d || [0, 0, 0]
      sphere.position.set(pos[0] || 0, pos[1] || 0, pos[2] || 0)
      sphere.userData.isSLAMObject = true
      scene.add(sphere)
    })
    
    console.log(`✅ 添加地图点: ${mappoints.length}个`)
  }
  
  renderer.render(scene, camera)
}

const startVisualization = async () => {
  loading.value = true
  try {
    console.log('🚀 开始可视化，会话ID:', props.sessionId)
    // 直接加载SLAM数据，不需要启动会话
    await loadSlamData()
    status.value = 'RUNNING'
    console.log('✅ 可视化启动成功')
  } catch (error) {
    console.error('❌ 启动可视化失败:', error)
    status.value = 'ERROR'
  } finally {
    loading.value = false
  }
}

const resetView = () => {
  if (camera) {
    camera.position.set(10, 10, 10)
    camera.lookAt(0, 0, 0)
    // controls.reset()
  }
}

const changeCameraView = () => {
  if (!camera) return
  
  switch (cameraView.value) {
    case 'perspective':
      camera.position.set(10, 10, 10)
      break
    case 'top':
      camera.position.set(0, 20, 0)
      camera.lookAt(0, 0, 0)
      break
    case 'side':
      camera.position.set(20, 0, 0)
      camera.lookAt(0, 0, 0)
      break
    case 'front':
      camera.position.set(0, 0, 20)
      camera.lookAt(0, 0, 0)
      break
  }
  // controls.update()
}

const toggleShowTrajectory = () => {
  showTrajectory.value = !showTrajectory.value
  updateVisualization()
}

const toggleShowKeyframes = () => {
  showKeyframes.value = !showKeyframes.value
  updateVisualization()
}

const toggleShowMappoints = () => {
  showMappoints.value = !showMappoints.value
  updateVisualization()
}

const toggleFullscreen = () => {
  if (!fullscreen.value) {
    enterFullscreen()
  } else {
    exitFullscreen()
  }
}

const enterFullscreen = () => {
  fullscreen.value = true
  document.body.style.overflow = 'hidden'
}

const exitFullscreen = () => {
  fullscreen.value = false
  document.body.style.overflow = 'auto'
}

const exportData = () => {
  const data = {
    trajectory,
    keyframes,
    mappoints,
    slamData: slamData.value,
    exportTime: new Date().toISOString()
  }
  
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `slam_3d_data_${props.sessionId}_${Date.now()}.json`
  a.click()
  URL.revokeObjectURL(url)
}

// 外部调用更新SLAM数据的方法
const updateSlamData = (data) => {
  console.log('🎯 外部调用更新SLAM数据:', data)
  
  if (data) {
    // 处理轨迹数据
    if (data.trajectoryPoints && Array.isArray(data.trajectoryPoints)) {
      trajectory = data.trajectoryPoints
    }
    
    // 处理关键帧数据
    if (data.keyframes && Array.isArray(data.keyframes)) {
      keyframes = data.keyframes
    }
    
    // 处理地图点数据
    if (data.mapPoints3D && Array.isArray(data.mapPoints3D)) {
      mappoints = data.mapPoints3D
    }
    
    // 更新SLAM数据统计
    slamData.value = {
      keyframes: data.keyframes || slamData.value.keyframes,
      mappoints: data.mapPoints || slamData.value.mappoints,
      trajectoryLength: data.trajectoryLength || slamData.value.trajectoryLength,
      progress: data.progress || slamData.value.progress
    }
    
    console.log('📊 更新后的数据:', {
      trajectory: trajectory.length,
      keyframes: keyframes.length,
      mappoints: mappoints.length,
      slamData: slamData.value
    })
    
    // 更新3D可视化
    updateVisualization()
  }
}

// 暴露方法给父组件
defineExpose({
  updateSlamData
})

// 不再需要监听sessionId变化，使用固定ID

// 自动刷新定时器
let refreshTimer = null

// 开始自动刷新
const startAutoRefresh = () => {
  if (refreshTimer) return
  
  refreshTimer = setInterval(async () => {
    // 只在状态为RUNNING且没有数据时才刷新（避免模拟数据重复加载）
    if (status.value === 'RUNNING' && !hasData.value) {
      await loadSlamData()
    }
  }, 5000) // 每5秒检查一次，但只在需要时刷新
}

// 停止自动刷新
const stopAutoRefresh = () => {
  if (refreshTimer) {
    clearInterval(refreshTimer)
    refreshTimer = null
  }
}

// 监听会话ID变化
watch(() => props.sessionId, async (newSessionId, oldSessionId) => {
  if (newSessionId && newSessionId !== oldSessionId) {
    console.log('🔄 会话ID变化:', oldSessionId, '->', newSessionId)
    // 重置数据
    hasData.value = false
    trajectory = []
    keyframes = []
    mappoints = []
    // 重新加载数据
    await loadSlamData()
  }
})

// 生命周期
onMounted(() => {
  nextTick(async () => {
    console.log('🎯 3D可视化组件挂载，会话ID:', props.sessionId)
    init3DScene()
    // 自动加载SLAM数据
    if (props.sessionId) {
      await loadSlamData()
    } else {
      console.warn('⚠️ 会话ID为空，无法加载数据')
    }
    startAutoRefresh()
  })
})

onUnmounted(() => {
  stopAutoRefresh()
})
</script>

<style scoped>
.slam-3d-visualization {
  width: 100%;
}

canvas {
  display: block;
}

/* 全屏样式 */
.fixed.inset-0 {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9999;
}
</style>
